#ifndef LEXER_H
#define LEXER_H
#include <regex>
#include <string>
#include <vector>

namespace LEXER
{
    enum TokenType
    {
        NUL,       // 空符号，无效符号，表示未初始化或占位符
        IDENT,     // 标识符，用户定义的变量、常量或过程名
        NUMBER,    // 数字常量，整数
        PLUS,      // 加号 "+"
        MINUS,     // 减号 "-"
        TIMES,     // 乘号 "*"
        SLASH,     // 除号 "/"
        ODDSYM,    // 奇偶判断符号 "odd"，用于条件语句中
        EQL,       // 等号 "="，表示判断相等
        NEQ,       // 不等号 "#"
        LSS,       // 小于号 "<"
        LEQ,       // 小于等于号 "<="
        GTR,       // 大于号 ">"
        GEQ,       // 大于等于号 ">="
        LPAREN,    // 左括号 "("
        RPAREN,    // 右括号 ")"
        COMMA,     // 逗号 ","
        SEMICOLON, // 分号 ";"
        PERIOD,    // 句号 "."，用于表示程序结束
        BECOMES,   // 赋值符号 ":="
        RBRACKET,  // 右中括号
        LBRACKET,  // 左中括号
        LBRACE,    // 左大括号
        RBRACE,    // 右大括号
        /* 保留字符号 */
        BEGINSYM, // "begin"，表示程序块的开始
        ENDSYM,   // "end"，表示程序块的结束
        IFSYM,    // "if"，表示条件语句
        ElSESYM,  // "else"，表示条件语句的分支
        THENSYM,  // "then"，用于条件语句的分支
        WHILESYM, // "while"，表示循环语句
        WRITESYM, // "write"，表示输出语句
        READSYM,  // "read"，表示输入语句
        DOSYM,    // "do"，用于循环语句的循环体
        CALLSYM,  // "call"，表示过程调用
        CONSTSYM, // "const"，表示常量声明
        VARSYM,   // "var"，表示变量声明
        PROCSYM,  // "procedure"，表示过程声明
        PROGRAM_END,
        UNKNOWN
    };

    
    inline std::ostream &operator<<(std::ostream &os, const TokenType &type)
    {
        switch (type)
        {
        case TokenType::NUL:
            os << "NUL";
            break;
        case TokenType::IDENT:
            os << "IDENT";
            break;
        case TokenType::NUMBER:
            os << "NUMBER";
            break;
        case TokenType::PLUS:
            os << "PLUS";
            break;
        case TokenType::MINUS:
            os << "MINUS";
            break;
        case TokenType::TIMES:
            os << "TIMES";
            break;
        case TokenType::SLASH:
            os << "SLASH";
            break;
        case TokenType::ODDSYM:
            os << "ODDSYM";
            break;
        case TokenType::EQL:
            os << "EQL";
            break;
        case TokenType::LBRACKET:
            os << "LBRACKET";
            break;
        case TokenType::RBRACKET:
            os << "RBRACKET";
            break;
        case TokenType::LBRACE:
            os << "LBRACE";
            break;
        case TokenType::RBRACE:
            os << "RBRACE";
            break;
        case TokenType::NEQ:
            os << "NEQ";
            break;
        case TokenType::LSS:
            os << "LSS";
            break;
        case TokenType::LEQ:
            os << "LEQ";
            break;
        case TokenType::ElSESYM:
            os << "ElSESYM";
            break;
        case TokenType::GTR:
            os << "GTR";
            break;
        case TokenType::GEQ:
            os << "GEQ";
            break;
        case TokenType::LPAREN:
            os << "LPAREN";
            break;
        case TokenType::RPAREN:
            os << "RPAREN";
            break;
        case TokenType::COMMA:
            os << "COMMA";
            break;
        case TokenType::SEMICOLON:
            os << "SEMICOLON";
            break;
        case TokenType::PERIOD:
            os << "PERIOD";
            break;
        case TokenType::BECOMES:
            os << "BECOMES";
            break;
        case TokenType::BEGINSYM:
            os << "BEGINSYM";
            break;
        case TokenType::ENDSYM:
            os << "ENDSYM";
            break;
        case TokenType::IFSYM:
            os << "IFSYM";
            break;
        case TokenType::THENSYM:
            os << "THENSYM";
            break;
        case TokenType::WHILESYM:
            os << "WHILESYM";
            break;
        case TokenType::WRITESYM:
            os << "WRITESYM";
            break;
        case TokenType::READSYM:
            os << "READSYM";
            break;
        case TokenType::DOSYM:
            os << "DOSYM";
            break;
        case TokenType::CALLSYM:
            os << "CALLSYM";
            break;
        case TokenType::CONSTSYM:
            os << "CONSTSYM";
            break;
        case TokenType::VARSYM:
            os << "VARSYM";
            break;
        case TokenType::PROCSYM:
            os << "PROCSYM";
            break;
        case TokenType::PROGRAM_END:
            os << "PROGRAM_END";
            break;
        case TokenType::UNKNOWN:
            os << "UNKNOWN";
            break;
        default:
            os << "INVALID";
            break;
        }
        return os;
    }

    class Token
    {
    private:
        TokenType type;
        std::string value;
        int line_num;
        int col_num;

    public:
        int get_line_num() const { return line_num; }
        int get_col_num() const { return col_num; }
        Token() : type(TokenType::UNKNOWN), value(""), line_num(0), col_num(0) {}
        Token(TokenType type, std::string value, int line_num, int col_num) : type(type), value(value), line_num(line_num), col_num(col_num) {}
        TokenType getType() const { return type; }
        std::string getValue() const { return value; }
        void set_value(std::string value) { this->value = value; }
        void set_type(TokenType type) { this->type = type; }
        friend std::ostream &operator<<(std::ostream &os, const Token &token)
        {
            os << "Token(Type: " << token.getType() << ", Value: \"" << token.getValue() << "\", Line: " << token.line_num << ", Column: " << token.col_num << ")";
            return os;
        }
    };

    class Lexer
    {
    private:
        std::string source;
        std::vector<Token> tokens;
        char currentChar;
        int currentLine;
        int currentColumn;
        void advance();
        void skipWhitespace();
        void skipComment();

        Token getNextToken();
        bool is_indent(std::string token);
        bool is_number(std::string token);
        std::unordered_map<std::string, TokenType> keywords = {
            {"const", TokenType::CONSTSYM},
            {"var", TokenType::VARSYM},
            {"procedure", TokenType::PROCSYM},
            {"begin", TokenType::BEGINSYM},
            {"end", TokenType::ENDSYM},
            {"call", TokenType::CALLSYM},
            {"if", TokenType::IFSYM},
            {"then", TokenType::THENSYM},
            {"while", TokenType::WHILESYM},
            {"do", TokenType::DOSYM},
            {"read", TokenType::READSYM},
            {"write", TokenType::WRITESYM}
        };

        std::unordered_map<std::string, TokenType> operators = {
            {"+", TokenType::PLUS},
            {"-", TokenType::MINUS},
            {"*", TokenType::TIMES},
            {"/", TokenType::SLASH},
            {"=", TokenType::EQL},
            {"#", TokenType::NEQ},
            {"<", TokenType::LSS},
            {"<=", TokenType::LEQ},
            {">", TokenType::GTR},
            {">=", TokenType::GEQ},
            {":=", TokenType::BECOMES},
            {"(", TokenType::LPAREN},
            {")", TokenType::RPAREN},
            {",", TokenType::COMMA},
            {";", TokenType::SEMICOLON},
            {".", TokenType::PERIOD},
            {"[", TokenType::LBRACKET},
            {"]", TokenType::RBRACKET},
            {"{",TokenType::LBRACE},
            {"}",TokenType::RBRACE},
        };

    public:
        Lexer(std::string source) : source(source), currentChar(source[0]), currentLine(1), currentColumn(1) {}
        std::vector<Token> lex(std::string source);
        TokenType get_token_type(std::string token);
        const std::regex tokenRegex{R"(^\s*((?:begin|end|if|then|while|write|read|do|else|call|const|\{|\}|var|procedure|>=|<=|\[|\]|!=|:=|[0-9]+|[a-zA-Z_][a-zA-Z0-9_]*|[+\-*/=<>(),;.\[\]])))"};
    };
}
#endif